package display;

import core.file.FileBroker;
import entity.DungeonEntity;
import entity.Orientation;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Image;
import java.io.IOException;
import java.util.HashMap;
import javax.swing.*;

import enviro.*;
import rougehack.*;

@SuppressWarnings("serial")
public class MapDisplay extends JPanel{
	
    // <editor-fold defaultstate="collapsed" desc="Member Variables">
	private Dungeon dungeon; // This is the dungeon currently loaded into the game
        boolean dungeonLoaded = false;  // Flag inidcates dungeon is loaded;
	private HashMap<String, Image> imagemap = new HashMap<String, Image>();
    // </editor-fold>
            
    // <editor-fold defaultstate="collapsed" desc="Constructor(s)">
        
        /**
	 * Constructor
	 * 
	 * This constructor sets up and activates the map display
	 */
        
        
	public MapDisplay() {
	}
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="API">
	/**
	 * Load Dungeon
	 * 
	 * This method is used to load a new dungeon into the map display
	 * @param dungeon
	 * @throws IOException 
	 */
        
     
	public void loadDungeon(Dungeon dungeon) throws IOException {
		
		// Assign the dungeon into the local dungeon variable.
		this.dungeon = dungeon;
		
		// Loop through and absorb all of the tile images,
                // as well as associated DungeonEntity images, in the dungeon
		// into a HashMap for caching.
		for (int x = 0 ; x < dungeon.getWidth(); x++) {
			for (int y = 0 ; y < dungeon.getHeight(); y++) {
				
				// get the name of the image and see if it is in our hashmap yet
                                Tile currentTile = dungeon.getTile(x,y);
                                String filename = currentTile.getImagepath();
				
				// scan hashmap for name - if its not there 
                                // we'll load the image into memory
				if (!(imagemap.containsKey(filename))) {
					
					// look out for io exceptions
					// Place the filename and the image into the imagemap
					try {
						imagemap.put(filename, FileBroker.readImage(filename));
					}
					catch (IOException ioe) {
						System.out.println("File load error.");
					}
				}
                                
                                // We will now check all of the tile's slots for 
                                // DungeonEntities which need graphic representation
                                scanTileForImages(currentTile);
                               
			}
		}
               
                dungeonLoaded = true;
                repaint();
	}
	
	public void paintComponent(Graphics g) {
           
            if (dungeonLoaded) {
		Graphics2D g2d = (Graphics2D) g;

		// iterate through the tiles on the map and draw them
		for (int x = 0 ; x < getDungeon().getWidth(); x++) {
			
			for (int y = 0 ; y < getDungeon().getHeight() ; y++) {
				
				//
				// Display tile in mathematically appointed position
				//
				
				// Figure out the top left corner of the tile
				Point topLeft = new Point((RougeHack.getGridWidth() * x),
						(RougeHack.getGridHeight() * y));
				
				// Absorb the image from the map
				String filename = getDungeon().getTile(x,y).getImagepath();
				Image tileImage = imagemap.get(filename);
				
				// Draw the absorbed image in the appropriate place
				g2d.drawImage(tileImage,
						x * RougeHack.getGridWidth(),
						y * RougeHack.getGridHeight(),
						this);
                                
                                // Draw the DungeonEntities tied to this tile
                                try {
                                
                                    g2d = paintTileEntities(g2d, x, y);
                                    
                                }
                                catch (IOException ioe){}
                                
                                
			}
		}		
            }
	}
        
    // </editor-fold>
        
    // <editor-fold defaultstate="collapsed" desc="Private Utilities">
        /**
         * Commit an image to the image map with using filepath
         * 
         * This method will make sure the path is not empty or null, 
         * verify that is is unique to prevent over-writing of existing values,
         * and then commit the change.
         * 
         * @param imagePath
         * 
         * @return 
         *   TRUE == The string was valid
         *   FALSE == The string was empty (this does NOT mean the operation failed!)
         */
        private boolean commitImage(String imagePath) throws IOException {
            
            boolean valid = false;
            
            if (!(imagePath.equals(""))) {
                             
                valid = true;
                
                if (!(imagemap.containsKey(imagePath))) {

                    imagemap.put(imagePath, FileBroker.readImage(imagePath));
                }
            }
            
            return valid;
        }
        
        
        /**
         * Scan the tile for Entities, and in turn scan those entities for images.
         * 
         * Images found will be added into local memory for speedy retreival
         * @param tile
         *   This is the tile that will be scanned
         * 
         * @return 
         *   Returns false if no valid strings were found (does not indicate 
         * operational failure - the return is used to report whether any images 
         * are specified.
         * 
         * @throws IOException 
         *   Throws an IO Exception if something bad went down during the read
         */
        private boolean scanTileForImages(Tile tile) throws IOException {
            
            // this is our flag for valid measurement
            boolean valid = false;
            
            // Scan each slot in the tile for images
            valid = (scanEntityForImages(tile.getDungeonEntity())); 
            return valid;
        }
        
        
        /**
         * Scan an entity for images, and add those images into local memory.
         * 
         * @param entity
         *   This is the DungeonEntity being scanned for images
         * 
         * @return 
         *   Returns false only if no filepaths are specified, otherwise reports true.
         *   (note that this does not indicate operational success or failure)
         * 
         * @throws IOException 
         *   Throws an IOException if something bad went down during the read.
         */
        private boolean scanEntityForImages(DungeonEntity entity) throws IOException {
            
            // We use this 'dummy' to compare unitilized entities, that way we
            // can recognize and reject uninitlized entities and skip past them.            
            // This is a flag to indicate whether or not anything was updated.
            boolean valid = false;
            
            // skip past uninit'd 
            if (entity == null) return false;
                            
            String thisPath = entity.getOrientationImagePath(Orientation.NORMAL);
            valid = (commitImage(thisPath) || valid == true) ? true : false;

            thisPath = entity.getOrientationImagePath(Orientation.DEGREES_90);
            valid = (commitImage(thisPath) || valid == true) ? true : false;

            thisPath = entity.getOrientationImagePath(Orientation.DEGREES_180);
            valid = (commitImage(thisPath) || valid == true) ? true : false;

            thisPath = entity.getOrientationImagePath(Orientation.DEGREES_270);
            valid = (commitImage(thisPath) || valid == true) ? true : false;
            
            return valid;
        }
        
        /**
         * Assist paint manager by painting all of the dungeon entities
         * on a given tile
         * 
         * @param graphics
         *   The graphics 'pane' on which this is to be drawn
         * @param position
         *   A Point object containing the grid coords of the tile
         * 
         * @return
         *   The altered graphics object is returned.
         */
        private Graphics2D paintTileEntitites(Graphics2D g2d, Point position) throws IOException {
            
            return paintTileEntities(g2d, position.x, position.y);
        }
        
        /**
         * Assist paint manager by painting all of the dungeon entities
         * on a given tile
         * 
         * @param graphics
         *   The graphics 'pane' on which this is to be drawn
         * @param x
         *   The x grid point of the tile
         * @param y
         *   The y grid point of the tile
         * 
         * @return
         *   The altered graphics object is returned.
         */
        private Graphics2D paintTileEntities(Graphics2D g2d, int x, int y) throws IOException {
            
            Tile scanningTile = dungeon.getTile(x,y);
            
            if (!(scanTileForImages(scanningTile))) return g2d;
            
            g2d = paintEntity(x, y, g2d, scanningTile);
            
            return g2d;
        }
        
        private Graphics2D paintEntity(int x, int y, Graphics2D g2d, Tile scanningTile) throws IOException {
            
            DungeonEntity scanningEntity = scanningTile.getDungeonEntity();
            
            if (scanningEntity == null) return g2d;
            
            if (scanEntityForImages(scanningEntity)) {
                
                if (!(scanningEntity.getOrientationImagePath(scanningEntity.getOrientation()).equals(""))) {

                    // nab the image from the map
                    String path = scanningEntity.getOrientationImagePath(
                            scanningEntity.getOrientation());
                    Image entityImage = imagemap.get(path);
                    
                    // draw the image on the plate
                    g2d.drawImage(entityImage,
                            x * RougeHack.getGridWidth(),
                            y * RougeHack.getGridHeight(),
                            this);
                    return g2d;
                }
            }
            
            return g2d;
        }
	
	/**
	 * STANDARD GET / SET METHODS
	 */
	private Dungeon getDungeon() { 
		return dungeon;
	}
        
    // </editor-fold>
	
}
